#!/usr/bin/env python
# -*- coding: utf-8 -*-
##
##	Copyright (C) 2007 manatlan
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
## by the Free Software Foundation; version 2 only.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
"""
feedBox2 - a python server for a freebox

pour V4 (notes) :
 - cf mapping dans common/template.py (pour mapper les touches)
 - plugin photo : remplacer generation de png -> gif
"""
import sys
import os

from wsgiref.simple_server import make_server

import cgi

sys.path.append(os.path.join(os.path.dirname(__file__),"common"))

from common.pilotvlc import PilotVlc
from common.fbconf import FeedConf
from common.template import Template
import traceback

class MyDebug:
    def __init__(self,app):
        self.app=app
    def __call__(self,environ,start_response):
        try:
            return [i for i in self.app(environ,start_response)]
        except :
            type,value,tb=sys.exc_info()
            sys.__excepthook__(type, value, tb)
            try:
                start_response('500 Internal Server Error', [('content-type', 'text/html')])
            except:
                pass
            return ["<h1>Error</h1>","<pre>"]+traceback.format_exception(type, value, tb)+["</pre>"]


# ==============================================================================
class FBS(object):
# ==============================================================================
    """
    __data
    """
    pathPlugins="plugins"
    
    @staticmethod
    def init(path,feedconf):
        FBS.__data={}
        FBS.__path=path
        
        
        def loadPlugins(path): # dans FBS.__data
            if os.path.isdir(path):
                sys.path.append(path)
                for i in os.listdir(path):
                    if i[0] not in (".","_"):
                        print "Load plugin %s" % i
                        folder = path+"/"+i
                        plug = __import__(i,globals(), locals())
                        
                        if hasattr(plug,"__title__")  \
                            and hasattr(plug,"__order__") \
                            and hasattr(plug,"main"):
                            
                            # associe un objet Conf à chaque plugin
                            plug.main.conf = feedconf[i]
                            
                            # associe une methode save() pour sauver la conf
                            obj = plug.main
                            value = feedconf.save
                            setattr(obj, "save", value.__get__(obj, type(obj)))
                            
                            try:
                                FBS.__data[i]=(
                                                plug.__order__,
                                                plug.__title__,
                                                plug.main(),
                                                i+"/index",
                                                folder
                                            )
                            except Exception,m:
                                print "plugin '%s' is bad ! (%s)" % (folder,m)
                                del(plug)
                        else:
                            print "plugin '%s' is not suitable !" % folder
                            del(plug)
                sys.path.remove(path)

        loadPlugins(FBS.pathPlugins)
        loadPlugins(os.path.join(feedconf.homeFolder,FBS.pathPlugins))
        
        # recupere le plugin home
        # et lui associe sa liste de plugin
        home=None
        ordres={}
        for i in FBS.__data:
            ordre,titre,instance,url,folder = FBS.__data[i]
            if ordre: # evite home
                ordres[ordre] = (titre,url)
                
        lo = ordres.keys()
        lo.sort()
        
        # associe a home, sa liste de plugin ([titre,url],[],...)
        FBS.menus = [ordres[i] for i in lo]


    def serveStatic(self,file,start_response):
        #print "serve static content",file
        ext = file.split(".")[-1].lower()
        if ext in ["gif"]:
            start_response('200 OK', [('content-type', 'image/gif')])
        elif ext in ["png"]:
            start_response('200 OK', [('content-type', 'image/png')])
        elif ext in ["js"]:
            start_response('200 OK', [('content-type', 'text/javascript')])
        else:
            start_response('200 OK', [('content-type', 'text/html')])
            
        return open(file,"rb").read()


    def wsgi(self,environ, start_response):
        path = environ["PATH_INFO"]

        page = environ["PATH_INFO"][1:]
        args=self.query(cgi.parse_qs(environ["QUERY_STRING"]))
        
        for i in args:
            args[i]=args[i].decode("iso-8859-15")

        Template.isFreeBox="freeplayer" in environ["REMOTE_HOST"]
        
        # determine l'objet demandé suivant le path/pattern "/sobj/page"
        _l = page.strip("/").split("/")
        one = _l[0]
        second = "/".join(_l[1:])
        if second:
            if one in FBS.__data:
                sobj = one
                page = second
            else:
                sobj = "home"
                page = second
        else:
            sobj = "home"
        if not page: page="index"

        staticFile = os.path.join(FBS.__path,"common","static",page)
        if os.path.isfile( staticFile ):
            # priorité absolue aux fichiers static !!!!!!!!!!
            yield self.serveStatic(staticFile,start_response)
        elif sobj=="home":
            # appel du menu principal
            if page in ["/","index","/index"]:
                start_response('200 OK', [('content-type', 'text/html')])
                array = FBS.menus
                yield Template({"panel_display":"FEEDBOX"},nom="common/templates/home.html")
            else:
                start_response('404 OK', [('content-type', 'text/html')])
                yield "404 not found %s !"%page
        else:
            # appel de plugins
            ordre,titre,instance,url,folder = FBS.__data[sobj]
            staticFile = os.path.join(FBS.__path,folder,page)
            if os.path.isfile( staticFile ):
                yield self.serveStatic(staticFile,start_response)
            else:                
                precPath = os.getcwdu()
                os.chdir(os.path.join(FBS.__path,folder))
                try:
                    page=page.split(".")[0] # enleve l'eventuel extension
                    
                    if hasattr(instance,page):
                        obj = getattr(instance,page)
                        
                        #if hasattr(obj,"content_type"):
                        #    # the page/method has a different content-type, use it
                        #    content_type=getattr(obj,"content_type")
                        #else:
                        #    # default ct
                        content_type="text/html"
                            
                        if args:
                            # if there are args, call with args
                            b=obj(*(),**(args))  # generator de Site."page"() 
                        else:
                            # if there aren't args, call with no args
                            b=obj()  # generator de Site."page"() 
                            
                        if hasattr(b,"next"):
                            # it's a generator()
                            # le premier yield doit renvoyer le content-type
                            buf = b.next()
                            
                            # le premier est le mime type
                            start_response('200 OK', [('content-type', buf)])
        
                            while True:
                                # fetch its yield statement
                                try:
                                    yield b.next()
                                except StopIteration:
                                    break
                                    
                        elif b!=None:
                            #if b.__class__.__name__=="Redirect":
                            #    self.send_response(b.code) 
                            #    self.send_header('Location', b.url) 
                            #    self.root.end_headers()                             
                            if type(b)==tuple and len(b)==2:
                                start_response('200 OK', [('content-type', b[0])])
                                yield str(b[1])
                            else:
                                start_response('200 OK', [('content-type', content_type)])
                                #print sobj,page,[b,]
                                yield str(b)
                        else:
                            start_response('404 OK', [('content-type', 'text/html')])
                            yield "404 not found %s !"%page
                       
                    else:
                        start_response('404 OK', [('content-type', 'text/html')])
                        yield "404 not found %s !"%page
                
                except Exception,e:
                    print Exception,":",e
                
                os.chdir(precPath)
            
                
        


    def query(self,parsedQuery):
        """Returns the QUERY dictionary, similar to the result of cgi.parse_qs
        except that :
        - if the key ends with [], returns the value (a Python list)
        - if not, returns a string, empty if the list is empty, or with the
        first value in the list"""
        res={}
        for item in parsedQuery.keys():
            value=parsedQuery[item] # a Python list
            if item.endswith("[]"):
                res[item[:-2]]=value
            else:
                if len(value)==0:
                    res[item]=''
                else:
                    res[item]=value[0]
        return res


if __name__ == "__main__":

    # assure l'execution dans le repertoire de feedbox
    path = os.path.split(sys.argv[0])[0]
    if path: os.chdir(path)
    
    # juste pour demarrer le pipe vers sh/cmd
    PilotVlc()
    
    # initialise feedbox (config file)
    feedconf=FeedConf() # conf object
    if feedconf.checkConf():
        
        # recupere la localisation de VLC (issu du conf de fb)
        PilotVlc.exe = feedconf.config.vlc
        
        # initialise le serveur, en chargeant les plugins
        FBS.init(os.getcwd(),feedconf)
        
        # et lance le serveur
        port=8080
        print "Serving at http://%s:%d"%("127.0.0.1",port)
        #FBS.serve_forever(port)
    
        f=FBS()
        try:
            make_server('', port, MyDebug(f.wsgi)).serve_forever()    
        except KeyboardInterrupt,SystemExit:
            print "bye"
